import os
import re

import numpy as np
import pandas as pd

from utils.util import shoelace_area
from scipy.spatial import ConvexHull
from scipy.interpolate import interp1d

waterPoint = []
lastMappID = 0

def getWaterPoints(mapID, otherMap='无'):
    global waterPoint
    global lastMappID
    if waterPoint and lastMappID == mapID: return waterPoint
    lastMappID = mapID
    if mapID == 1:
        excel_file = '..\\data\\4-拙政园数据坐标.xlsx'
    elif mapID == 2:
        excel_file = '..\\data\\4-留园数据坐标.xlsx'
    elif mapID == 3:
        excel_file = '..\\data\\4-寄畅园数据坐标.xlsx'
    elif mapID == 4:
        excel_file = '..\\data\\4-瞻园数据坐标.xlsx'
    elif mapID == 5:
        excel_file = '..\\data\\4-豫园数据坐标.xlsx'
    elif mapID == 6:
        excel_file = '..\\data\\4-秋霞圃数据坐标.xlsx'
    elif mapID == 7:
        excel_file = '..\\data\\4-沈园数据坐标.xlsx'
    elif mapID == 8:
        excel_file = '..\\data\\4-怡园数据坐标.xlsx'
    elif mapID == 9:
        excel_file = '..\\data\\4-耦园数据坐标.xlsx'
    elif mapID == 10:
        excel_file = '..\\data\\4-绮园数据坐标.xlsx'
    else:
        excel_file = os.path.join('..\\data\\', otherMap)
    # 读取Excel的“道路”工作表
    df = pd.read_excel(excel_file, sheet_name='水体')

    # 提取第一列数据（区分线段的坐标）
    raw_data = df.iloc[:, 0].dropna().astype(str)
    segments = []  # 存储所有线段
    current_segment = []  # 当前线段点列表

    for item in raw_data:
        if item.startswith('            {0;'):
            # 新线段开始：保存当前线段（如果有），并重置
            if current_segment:
                segments.append(current_segment)
            current_segment = []
            segment_id = int(re.findall(r'\d+', item)[0])  # 提取线段ID
        else:

            # 解析坐标点：格式为 "b.{x,y,z}"

            if mapID == 6 or mapID == 8:
                match = re.match(r'\{([-\d.]+), ([-\d.]+), ([-\d.]+)\}', item)
                if match:
                    x = float(match.group(1))
                    y = float(match.group(2))
                    current_segment.append((x, y))
            elif mapID > 10:
                match = re.match(r'\(([-\d.]+), ([-\d.]+), ([-\d.]+)\)', item)
                if match:
                    x = float(match.group(1))
                    y = float(match.group(2))
                    current_segment.append((x, y))
            else:
                match = re.match(r'(\d+)\. \{([-\d.]+), ([-\d.]+), ([-\d.]+)\}', item)
                if match:
                    b = int(match.group(1))
                    x = float(match.group(2))
                    y = float(match.group(3))
                    current_segment.append((x, y))
    if current_segment:
        segments.append(current_segment)
    waterPoint = segments
    return segments


def generate_internal_points(boundary_points, grid_size=500):
    """
    生成多边形内部的均匀分布点

    参数:
    boundary_points: 边界点列表，格式 [(x1, y1), (x2, y2), ...]
    grid_size: 网格大小（可选）(grid_size = 100 意味着每0.5米插一个点)
    num_points: 期望的内部点数量（可选）

    返回:
    internal_points: 内部点列表
    """
    # 确保边界点是numpy数组格式
    boundary_points = np.array(boundary_points)

    # 计算凸包以获得更规则的边界[2](@ref)
    if len(boundary_points) > 3:
        hull = ConvexHull(boundary_points)
        boundary_points = boundary_points[hull.vertices]

    # 确保多边形闭合[4,6](@ref)
    if not np.array_equal(boundary_points[0], boundary_points[-1]):
        boundary_points = np.vstack([boundary_points, boundary_points[0]])

    # 提取坐标
    x_coords = boundary_points[:, 0]
    y_coords = boundary_points[:, 1]

    # 计算边界范围
    x_min, x_max = np.min(x_coords), np.max(x_coords)
    y_min, y_max = np.min(y_coords), np.max(y_coords)

    # 生成网格点
    x_grid = np.arange(x_min, x_max, grid_size)
    y_grid = np.arange(y_min, y_max, grid_size)
    grid_points = np.array([[x, y] for x in x_grid for y in y_grid])

    # 点-in-多边形检测（射线法）
    internal_points = []
    for point in grid_points:
        crossings = 0
        n = len(boundary_points)

        for i in range(n - 1):
            x1, y1 = boundary_points[i]
            x2, y2 = boundary_points[i + 1]

            # 检查点是否在边的y范围内
            if ((y1 > point[1]) != (y2 > point[1])):
                # 计算交点x坐标
                x_intersect = (point[1] - y1) * (x2 - x1) / (y2 - y1) + x1

                if point[0] < x_intersect:
                    crossings += 1

        if crossings % 2 == 1:  # 奇数次交叉表示内部
            internal_points.append(point)

    return np.array(internal_points), boundary_points


def getWaterAreaAndCount(mapID, wall_kdtree, tree_kdtree, mount_kdtree):
    allWaterPoints = getWaterPoints(mapID)
    if len(allWaterPoints) == 1:
        #如果只有一条水系
        return (shoelace_area(allWaterPoints[0]), 1)
    else:
        area = 0
        count = 0
        for waterLine in allWaterPoints:
            if len(waterLine) < 10: continue
            # 首先判断这个区域是‘水’还是‘岛’
            internalPoints, xxx = generate_internal_points(waterLine)
            flag = 0
            for internalPoint in internalPoints:
                dist, idx = wall_kdtree.query((internalPoint[0], internalPoint[1]), k=1)
                if dist < 500:
                    flag = flag + 1
                    continue
                dist, idx = tree_kdtree.query((internalPoint[0], internalPoint[1]), k=1)
                if dist < 50:
                    flag = flag + 1
                    continue
                dist, idx = mount_kdtree.query((internalPoint[0], internalPoint[1]), k=1)
                if dist < 50:
                    flag = flag + 1
                    continue
            if flag / len(internalPoints) < 0.2:
                # 如果'陆地率'小于50%，视为'水'
                area = area + shoelace_area(waterLine)
                count = count + 1
            else:
                # 如果'陆地率'大于50%，视为'岛'
                area = area - shoelace_area(waterLine)
        return area, count


def getWaterPerimeter(mapID):
    total_length = 0
    for line in getWaterPoints(mapID):
        # 将点集转换为NumPy数组（如果输入不是数组的话）
        points_arr = np.array(line)
        # 计算相邻点之间的差值 (x_{i+1} - x_i, y_{i+1} - y_i)
        diffs = np.diff(points_arr, axis=0)
        # 计算每个差值的欧几里得距离（二范数）
        distances = np.linalg.norm(diffs, axis=1)
        # 对所有距离求和
        total_length = total_length + np.sum(distances)
    return total_length
